/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "scan_callback.h"
#include "napi_scan_utils.h"
#include "scan_log.h"
#include <uv.h>

namespace OHOS::Scan {
struct CallbackParam {
    napi_env env;
    napi_ref ref;
    std::mutex* mutexPtr;
    uint32_t state;

    bool isGetSucc;
    int32_t sizeRead;
    int32_t scanVersion;
    
    ScanDeviceInfoTCP deviceInfoTCP;
    ScanDeviceInfoLibUSB devoceInfoLibUSB;
};

ScanCallback::ScanCallback(napi_env env, napi_ref ref) : env_(env), ref_(ref)
{
}

ScanCallback::~ScanCallback()
{
    std::lock_guard<std::mutex> autoLock(mutex_);
    SCAN_HILOGD("callback has been destroyed");
    NapiScanUtils::DeleteReference(env_, ref_);
    ref_ = nullptr;
}

bool ScanCallback::OnCallback(uint32_t state, const ScanDeviceInfoTCP &info)
{
    SCAN_HILOGE("GetScannerList Enter ScanCallback::OnCallback");
    SCAN_HILOGD("Scan deviceINfo callback in");
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    if (loop == nullptr) {
        SCAN_HILOGE("Failed to get uv event loop");
        return false;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        SCAN_HILOGE("Failed to create uv work");
        return false;
    }
    CallbackParam *param = new (std::nothrow) CallbackParam;
    if (param == nullptr) {
        SCAN_HILOGE("Failed to create callback parameter");
        return false;
    }
    {
        std::lock_guard<std::mutex> lock(mutex_);
        param->env = env_;
        param->ref = ref_;
        param->mutexPtr = &mutex_;
        param->state = state;
        param->deviceInfoTCP = info;
    }
    work->data = param;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int statusInt) {
            CallbackParam *cbParam = static_cast<CallbackParam*>(work->data);
            if (cbParam != nullptr) {
                std::lock_guard<std::mutex> autoLock(*cbParam->mutexPtr);
                napi_value callbackFunc = NapiScanUtils::GetReference(cbParam->env, cbParam->ref);
                napi_value callbackResult = nullptr;
                napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
                callbackValues[0] = ScannerInfoHelperTCP::MakeJsObject(cbParam->env,cbParam->deviceInfoTCP);
                napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
                    callbackValues, &callbackResult);
                SCAN_HILOGE("run napi call deviceInfo callback fun success");
                if (work != nullptr) {
                    delete work;
                    work = nullptr;
                }
                delete cbParam;
                cbParam = nullptr;
            }
        });
    return true;
}

bool ScanCallback::OnCallback(uint32_t state, const ScanDeviceInfoLibUSB &info)
{
    SCAN_HILOGE("GetScannerList Enter ScanCallback::OnCallback");
    SCAN_HILOGD("Scan deviceINfo callback in");
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    if (loop == nullptr) {
        SCAN_HILOGE("Failed to get uv event loop");
        return false;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        SCAN_HILOGE("Failed to create uv work");
        return false;
    }
    CallbackParam *param = new (std::nothrow) CallbackParam;
    if (param == nullptr) {
        SCAN_HILOGE("Failed to create callback parameter");
        return false;
    }
    {
        std::lock_guard<std::mutex> lock(mutex_);
        param->env = env_;
        param->ref = ref_;
        param->mutexPtr = &mutex_;
        param->state = state;
        param->devoceInfoLibUSB = info;
    }
    work->data = param;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int statusInt) {
            CallbackParam *cbParam = static_cast<CallbackParam*>(work->data);
            if (cbParam != nullptr) {
                std::lock_guard<std::mutex> autoLock(*cbParam->mutexPtr);
                napi_value callbackFunc = NapiScanUtils::GetReference(cbParam->env, cbParam->ref);
                napi_value callbackResult = nullptr;
                napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
                callbackValues[0] = ScannerInfoHelperLibUSB::MakeJsObject(cbParam->env,cbParam->devoceInfoLibUSB);
                napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
                    callbackValues, &callbackResult);
                SCAN_HILOGE("run napi call deviceInfo callback fun success");
                if (work != nullptr) {
                    delete work;
                    work = nullptr;
                }
                delete cbParam;
                cbParam = nullptr;
            }
        });
    return true;
}

bool ScanCallback::OnGetFrameResCallback(bool isGetSucc, int32_t sizeRead)
{
    SCAN_HILOGD("Scan OnGetFrameSucc callback in");
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    if (loop == nullptr) {
        SCAN_HILOGE("Failed to get uv event loop");
        return false;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        SCAN_HILOGE("Failed to create uv work");
        return false;
    }
    CallbackParam *param = new (std::nothrow) CallbackParam;
    if (param == nullptr) {
        SCAN_HILOGE("Failed to create callback parameter");
        return false;
    }
    {
        std::lock_guard<std::mutex> lock(mutex_);
        param->env = env_;
        param->ref = ref_;
        param->mutexPtr = &mutex_;
        param->isGetSucc = isGetSucc;
        param->sizeRead = sizeRead;
    }
    work->data = param;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int statusInt) {
            CallbackParam *cbParam = static_cast<CallbackParam*>(work->data);
            if (cbParam != nullptr) {
                std::lock_guard<std::mutex> autoLock(*cbParam->mutexPtr);
                napi_value callbackFunc = NapiScanUtils::GetReference(cbParam->env, cbParam->ref);
                napi_value callbackResult = nullptr;
                napi_value callbackValues[NapiScanUtils::ARGC_TWO] = { 0 };
                callbackValues[0] = NapiScanUtils::CreateBoolean(cbParam->env, cbParam->isGetSucc);
                callbackValues[1] = NapiScanUtils::CreateInt32(cbParam->env, cbParam->sizeRead);
                napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_TWO,
                    callbackValues, &callbackResult);
                SCAN_HILOGD("run napi call OnGetFrameResCallback callback fun success");
                if (work != nullptr) {
                    delete work;
                    work = nullptr;
                }
                delete cbParam;
                cbParam = nullptr;
            }
        });
    return true;
}

bool ScanCallback::OnScanInitCallback(int32_t &scanVersion)
{
    SCAN_HILOGD("Enter Scan OnScanInitCallback callback ");
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(env_, &loop);
    if (loop == nullptr) {
        SCAN_HILOGE("Failed to get uv event loop");
        return false;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        SCAN_HILOGE("Failed to create uv work");
        return false;
    }
    CallbackParam *param = new (std::nothrow) CallbackParam;
    if (param == nullptr) {
        SCAN_HILOGE("Failed to create callback parameter");
        return false;
    }
    {
        std::lock_guard<std::mutex> lock(mutex_);
        param->env = env_;
        param->ref = ref_;
        param->mutexPtr = &mutex_;
        param->scanVersion = scanVersion;
    }
    work->data = param;
    uv_queue_work(
        loop, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int statusInt) {
            CallbackParam *cbParam = static_cast<CallbackParam*>(work->data);
            if (cbParam != nullptr) {
                std::lock_guard<std::mutex> autoLock(*cbParam->mutexPtr);
                napi_value callbackFunc = NapiScanUtils::GetReference(cbParam->env, cbParam->ref);
                napi_value callbackResult = nullptr;
                napi_value callbackValues[NapiScanUtils::ARGC_ONE] = { 0 };
                callbackValues[0] = NapiScanUtils::CreateInt32(cbParam->env, cbParam->scanVersion);
                napi_call_function(cbParam->env, nullptr, callbackFunc, NapiScanUtils::ARGC_ONE,
                    callbackValues, &callbackResult);
                SCAN_HILOGD("run napi call OnScanInitCallback callback fun success");
                if (work != nullptr) {
                    delete work;
                    work = nullptr;
                }
                delete cbParam;
                cbParam = nullptr;
            }
        });
    return true;
}

} // namespace OHOS::Scan
